﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Diagnostics;
using System.IO;
using StarfleetCommander.Models;
using System.Text;
using System.Web.Script.Serialization;
using BMvcReusable;
using StarfleetCommander.Persistence.Gen3;
using StarfleetCommander.Persistence;
using JavaScriptSupport;
using StarfleetCommander.Models.Recording;

namespace StarfleetCommander.Controllers
{
	public partial class RecorderController : Controller
	{


		public const string XmlBegin = "<?xml version=\"1.0\"?>";
		public const string MimeXml = "text/xml";
		public const string MimePlain = "text/plain";



		private const string STR_AccessControlAllowOrigin = "Access-Control-Allow-Origin";


		// private delegate void SaveHtmlDelegate(GalaxyRecorder recorder);
		private delegate int SaveHtmlDelegate2(GalaxyRecorder2 recorder);


		public virtual JavaScriptResult Recorder()
		{
			var url = Url.AppBase(Request);
			Debug.Assert(url.EndsWith("/"));
			var text = System.IO.File.ReadAllText(Server.MapPath(MvcLinks.Scripts.ClientScripts.AjaxXmlPiped_js)).Replace("@pipeline", url);
			//var minifier = new JavaScriptSupport.JavaScriptMinifier();
			var sb = new StringBuilder();
			using (var sr=new StringReader(text))
			using (var sw=new StringWriter(sb))
			JavaScriptMinifier.Minify(sr, sw);
			return JavaScript(sb.ToString());

		}

		[Authorize]
		public virtual ActionResult Index()
		{
			
			var token= (Request.IsAuthenticated && User!=null && User.Identity!=null) ? UserPersistance.GetUserToken(User.Identity) : (Guid?)null; 
			var bookmarklets = new List<Models.ModelBookmarklet>();
			bookmarklets.Add(new ModelBookmarklet("RecorderXml",
				MvcLinks.Scripts.ClientScripts.AjaxXmlHttp_js, true, "The main recorder Starfleet Explorer, with debris calculator"));
			bookmarklets.Add(new ModelBookmarklet("RecorderDebrisGalaxyTrio",
				MvcLinks.Scripts.ClientScripts.RecorderDebrisGalaxyTrio_js, true, "The main recorder Starfleet Explorer,with debris calculator and galaxy explorer all built in."));
			bookmarklets.Add(new ModelBookmarklet("EspionageRecorder", MvcLinks.Scripts.ClientScripts.AjaxEspionage_js, true, "Records all espionage in your inbox that's less than an hour old"));
#if DEBUG
			bookmarklets.Add(new ModelBookmarklet("RecorderPipelined", Url.Action(MvcT4.Recorder.Recorder()), true, "Recorder for testing against current pipeline"));
#endif
			//bookmarklets.Add(new ModelBookmarklet("RecorderLegacy",
			//    BookmarkletScriptPath + "Recorder.js","The older recorder, worked in IE and Chrome but not firefox"));
			return View(new Models.ViewModel.BookmarkletViewModel(token, bookmarklets));
		}

		
		[Authorize]
		public virtual ActionResult MyLog(string userGuid)
		{


			if (ErrorRecorder.DefaultPathExists(Server.MapPath) == false)
			{
				return ErrorRedirectToIndex("Error log directory not found:" + ErrorRecorder.basePath);
			}
			Guid guid;
			if (Guid.TryParse(userGuid, out guid) == false)
				return ErrorRedirectToIndex("Failed to read user identifier");
			var logFile = ErrorRecorder.GetLastErrorLog(Server.MapPath, guid);
			if (logFile == null)
			{
				return ErrorRedirectToIndex("No error file found for you in " + ErrorRecorder.basePath);
			}

			return Content(System.IO.File.ReadAllText(logFile.FullName));

		}

		private RedirectToRouteResult ErrorRedirectToIndex(string errorText)
		{
			TempData[HtmlHelpers.StatusKey] = errorText;
			return RedirectToAction(MvcT4.Recorder.Index());
		}

		[Authorize]
		public virtual ContentResult LastRecorded(Guid? token = null)
		{
			Response.AddHeader(STR_AccessControlAllowOrigin, "*");
			Guid? userToken = token;
			if (token.HasValue == false)
				userToken = new Guid(Request.QueryString[Models.Recording.ModelSerialization.STR_Token]);
			var result = UserPersistance.GetLastRecorded(token.Value);
			return Content(result);
		}

		private void testRecorderPathAccess()
		{
			var path = ErrorRecorder.GetDefaultOutPath(Server.MapPath);
			var testFile = System.IO.Path.Combine(path, "testfile.txt");
			using (var output = new System.IO.StreamWriter(testFile, false))
				output.WriteLine("test success at " + DateTime.UtcNow.ToLongDateString());

		}
		[Authorize(Roles = "SfcAdmin")]
		public virtual ActionResult Last()
		{
			
			if (ErrorRecorder.DefaultPathExists(Server.MapPath) == false)
				return ErrorRedirectToIndex("Log Directory not found:" + ErrorRecorder.GetDefaultOutPath(Server.MapPath));
			//TODO: move this to the errorRecorder
			//var anyFilesExist = System.IO.Directory.GetFiles(path).Any() ;
			try
			{
				testRecorderPathAccess();
			}
			catch (Exception ex)
			{
				MvcApplication.LogException(ex);
				//TODO: log somewhere?
				return ErrorRedirectToIndex("Recorder can not write log files");
			}

			var l = (from files in Directory.GetFiles(ErrorRecorder.GetDefaultOutPath(Server.MapPath))
					 let info = new FileInfo(files)
					 orderby info.LastWriteTimeUtc descending
					 select info).FirstOrDefault();


			if (l != null)
			{
				//http://stackoverflow.com/questions/134905/what-is-the-best-way-to-return-xml-from-a-controllers-action-in-asp-net-mvc
				//HttpContext.Response.Clear();

				//Response.ContentType = MimeXml;

				var data = String.Format("{0}\r\n{1}",
							   l.LastWriteTimeUtc.ToString().WrapXmlInTag("utc"),
							   System.IO.File.ReadAllText(l.FullName).WrapXmlInTag("content"));
				return Content(XmlBegin + data.BuildXmlTag("last").MergeAttributeFl("filename", l.Name)
					.MergeAttributeFl("relativePath", ErrorRecorder.basePath).ToString());
			}

			return Content("No files found:" + ErrorRecorder.GetDefaultOutPath(Server.MapPath));
		}



		/// <summary>
		/// Gen3 save method
		/// </summary>
		/// <returns></returns>
		[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
		public virtual ContentResult Save()
		{
            Response.AddHeader(STR_AccessControlAllowOrigin, "*");
			//Guid? userToken, GalaxyRecorder recorder
			RecorderToken token;
			GalaxyRecorder2 recorder;
			try {

			ProcessRequest2(ErrorRecorder.GetDefaultOutPath(Server.MapPath), out token, out recorder);
			}
			catch (Exception exception)
			{
				MvcApplication.LogException(exception);
				Response.StatusCode = 500;

				return Content("failed");
			}
			if (recorder == null || token == null || token.TokenGuid.HasValue == false )
			{
				Response.StatusCode = 400;// internal error
				
				return null;
			}
			if(token.TokenGuid.Value==Guid.Empty)
			{
				Response.StatusCode=409; //bad request
				return null;
			}
		   
				return SaveHtmlWrapper2(token, recorder, (gr) => gr.Save());
			
			
			
		}

		[AcceptVerbs(HttpVerbs.Post | HttpVerbs.Get)]
		public virtual ContentResult SaveHtml()
		{
			return Save();
		}

        private bool IsInvalidGen3Request()
        {
            #warning request no longer supported
            return (Request.Form.Count > 0 &&
                    Request.Form[Models.Recording.ModelSerialization.STR_Token].IsNullOrEmpty()
                    == false && Request.Form[Models.Recording.ModelSerialization.STR_Planets].IsNullOrEmpty() == false
                    && Request.Form[Models.Recording.ModelSerialization.STR_Universe].IsNullOrEmpty() == false);
            //token = new RecorderToken(Request.Form[Models.Recording.ModelSerialization.STR_Token]);
            ////ErrorRecorder. LogDelete(token.SavePath);
            //recorder = new GalaxyRecorder2(token.TokenGuid.Value,Request.Form[Models.Recording.ModelSerialization.IsStandard],
            //    Request.Form[Models.Recording.ModelSerialization.STR_Planets]);
        }

		private void ProcessRequest2(string mappedBasePath, out RecorderToken token, out GalaxyRecorder2 recorder)
		{
            if(System.Diagnostics.Debugger.IsAttached)
			Debug.Assert(mappedBasePath.StartsWith("~") == false);

            if (IsInvalidGen3Request())
			{
				MvcApplication.Log("Unsupported request type submitted");
				recorder = null;
				token = null;
				return;
			}

			if (Request.ContentType.StartsWith(MimePlain, StringComparison.CurrentCultureIgnoreCase) &&
                Request.InputStream.Length > 0 && Request.InputStream.Length + 2 < int.MaxValue)
			{
				int counter, strRead, strLen;
				strLen = Convert.ToInt32(Request.InputStream.Length);
				var sb = new StringBuilder(strLen);

				byte[] strArr = new byte[Request.InputStream.Length];

				strRead = Request.InputStream.Read(strArr, 0, strLen);

				for (counter = 0; counter < strLen; counter++)
					sb.Append((char)strArr[counter]);
				ModelSerialization.IntelPost IntelPost = ModelSerialization.Deserialize(sb.ToString());
				token = new RecorderToken(IntelPost.Token);
				//LogDelete(token.SavePath);
				recorder = new GalaxyRecorder2( token.TokenGuid.Value, IntelPost.HtmlUniverse, IntelPost.Planets);

				return;
			}
			recorder = null;

			token = new RecorderToken(null);
			//ErrorRecorder.Log(ErrorRecorders.SnapRecorder, Server.MapPath, "Planets or Token was missing".WrapInTag("formvars"), true);
			MvcApplication.Log("Planets or token was missing");

		}

		private ContentResult SaveHtmlWrapper2(RecorderToken token, GalaxyRecorder2 galaxyRecorder, SaveHtmlDelegate2 processAction)
		{
			const string success = "Saved";
			Response.AddHeader(STR_AccessControlAllowOrigin, "*");
			ContentResult result;
			try
			{
				result = Content(success + " " + processAction(galaxyRecorder) + " snapshots");
			}
			catch (Exception exception)
			{
				//ErrorRecorder.Log(ErrorRecorders.SnapRecorder, Server.MapPath, exception.ToString().WrapInTag("exception"), true, specifier: token.TokenGuid.ToString());
				MvcApplication.LogException(exception);
				Response.StatusCode = 505;

				return Content("failed");
			}

			// result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
			// Log(token.SavePath, result.Data.ToString().WrapInTag("response"), true);
			return result;
		}

		/// <summary>
		/// Save espionage report
		/// </summary>
		/// <returns></returns>
		public virtual ContentResult SaveEspionage()
		{

			Response.AddHeader(STR_AccessControlAllowOrigin, "*");
			try
			{
				//pull results out of parse
				if (Request.ContentType.StartsWith(MimePlain, StringComparison.CurrentCultureIgnoreCase)
						&& Request.InputStream.Length > 0 && Request.InputStream.Length + 2 < int.MaxValue)
				{
					int counter, strRead, strLen;
					strLen = Convert.ToInt32(Request.InputStream.Length);
					var sb = new StringBuilder(strLen);

					byte[] strArr = new byte[Request.InputStream.Length];

					strRead = Request.InputStream.Read(strArr, 0, strLen);

					for (counter = 0; counter < strLen; counter++)
						sb.Append((char)strArr[counter]);
					var esp = Models.Recording.ModelSerialization.DeserializeEsp(sb.ToString());
					Debug.Assert(esp != null);
					if (esp.Esp.Count() == 0)
						return Content("Espionage Called but no reports found");
					var token = new RecorderToken(esp.Token);
					var recorder = new EspionageRecorder(Server.MapPath, esp, esp.HtmlUniverse, token.TokenGuid.Value);
					try
					{
						var count = recorder.Save(Server.MapPath);
						return Content("Espionage Success:" + count + " new reports");
					}
					catch (Exception exRecord)
					{
						//ErrorRecorder.Log(ErrorRecorders.EspRecorder, Server.MapPath, "Espionage Failed:" + exRecord.ToString(), false, specifier: token.TokenGuid.ToString());
						MvcApplication.LogException(exRecord);
						return Content("Esionage Record failed:" + exRecord.Message);
					}

				}
				return Content("Failed to locate post data");
			}
			catch (Exception exParse)
			{
				//ErrorRecorder.Log(ErrorRecorders.EspRecorder, Server.MapPath, "Parse failed:" + exParse.ToString(), false);
				MvcApplication.LogException(exParse);
				return Content("Espionage Parse failed:" + exParse.Message);
			}


		}




		private class RecorderToken
		{
			public Guid? TokenGuid { get { return _guid; } }
			
			private readonly string _formValue;
			private readonly Guid? _guid;
			
			public RecorderToken(string formValue)
			{
				
				_formValue = formValue;
				if (formValue.IsNullOrEmpty() == false)
				{
					_guid = new Guid(formValue);
					
				}
				
			}

		}
	}
}
